/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2009: ngi-central.org                                       */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements the quadrilateral geometric primitive.

#ifndef __sgl_quadrilateral_h__
#define __sgl_quadrilateral_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/geometric_primitive.h>
#include <sgl/point.h>
#include <boost/static_assert.hpp>
#include <boost/operators.hpp>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* quadrilateral                                                              */
/*                                                                            */
/******************************************************************************/

template <
	/// The dimensionality. 
	//!
	//! This defaults to two dimensions.
	size_t D=2,
	/// The element type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), 
	//! but any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// A quadrilateral in euclidean space.
//!
//! A quadrilateral represents a polygon with 4 vertcies.
class quadrilateral
	/// A quadrilateral is a geometric primitive.
	: public geometric_primitive<D,T>
	, boost::equality_comparable<sgl::quadrilateral<D,T>, sgl::quadrilateral<D,T>
	, boost::additive<sgl::quadrilateral<D,T>, sgl::vector<D,T>
	> >
{
	// A quadrilateral must have two or more dimensions.
	BOOST_STATIC_ASSERT(D >= 2);

public:
	/// Default constructor.
	//!
	//! Constructs a zero quadrilateral.
	quadrilateral()
		: p1_()
		, p2_()
		, p3_()
		, p4_()
	{ }

	/// Construct a quadrilateral from three points.
	quadrilateral(
		/// The first point of the quadrilateral.
		sgl::point<D,T> p1,
		/// The second point of the quadrilateral.
		sgl::point<D,T> p2,
		/// The third point of the quadrilateral.
		sgl::point<D,T> p3,
		/// The fourth point of the quadrilateral.
		sgl::point<D,T> p4)
		: p1_(p1)
		, p2_(p2)
		, p3_(p3)
		, p4_(p4)
	{ }

	/// Copy constructor.
	quadrilateral(
		/// The right hand side.
		sgl::quadrilateral<D,T> const& rhs)
		: p1_(rhs.p1_)
		, p2_(rhs.p2_)
		, p3_(rhs.p3_)
		, p4_(rhs.p4_)
	{ }

	/// Copy assignment operator.
	sgl::quadrilateral<D,T> & operator = (
		/// The right hand side.
		sgl::quadrilateral<D,T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges the elements of two quadrilateral objects.
	void swap(
		/// The other quadrilateral.
		sgl::quadrilateral<D,T> & rhs)
	{
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::swap(p1_, rhs.p1_);
		std::swap(p2_, rhs.p2_);
		std::swap(p3_, rhs.p3_);
		std::swap(p4_, rhs.p4_);
	}

	/// Find the type of the geometric primitive.
	//!
	//! /return The type of the primitive.
	geometric_type type() const
	{
		return sgl::geometric_primitive<D,T>::quadrilateral;
	} 

	/// Access the first point of the quadrilateral.
	//!
	//! /return Returns a reference to the first point of the quadrilateral.
	sgl::point<D,T> & p1()
	{
		return p1_;
	}

	/// Access the first point of the quadrilateral.
	//!
	//! /return Returns a const reference to the first point of the quadrilateral.
	sgl::point<D,T> const & p1() const
	{
		return p1_;
	}

	/// Access the second point of the quadrilateral.
	//!
	//! /return Returns a reference to the second point of the quadrilateral.
	sgl::point<D,T> & p2()
	{
		return p2_;
	}

	/// Access the second point of the quadrilateral.
	//!
	//! /return Returns a const reference to the second point of the quadrilateral.
	sgl::point<D,T> const & p2() const
	{
		return p2_;
	}

	/// Access the third point of the quadrilateral.
	//!
	//! /return Returns a reference to the third point of the quadrilateral.
	sgl::point<D,T> & p3()
	{
		return p3_;
	}

	/// Access the third point of the quadrilateral.
	//!
	//! /return Returns a const reference to the third point of the quadrilateral.
	sgl::point<D,T> const & p3() const
	{
		return p3_;
	}

	/// Access the fourth point of the quadrilateral.
	//!
	//! /return Returns a reference to the fourth point of the quadrilateral.
	sgl::point<D,T> & p4()
	{
		return p4_;
	}

	/// Access the fourth point of the quadrilateral.
	//!
	//! /return Returns a const reference to the fourth point of the quadrilateral.
	sgl::point<D,T> const & p4() const
	{
		return p4_;
	}

	/// Equality operator.
	//!
	//! Two quadrilaterals are compared for equality by comparing their vertices 
	//! in a pairwise manner. The quadrilaterals are equal only if all pairwise 
	//! comparisons are equal.
	//!
	//! Through the use of boost::equality as a base class operator != is 
	//! defined as well and implemented in terms of this operator ==.
	//!
	//! \return true if equal, false otherwise.
    bool operator == (
		/// Right hand side.
		sgl::quadrilateral<D,T> const& rhs) const
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		return 
			p1_ == rhs.p1_ &&
			p2_ == rhs.p2_ &&
			p3_ == rhs.p3_ &&
			p4_ == rhs.p4_;
    }

	/// Add a vector to a quadrilateral.
	//!
	//! Adding a vector to a quadrilateral moves the quadrilateral in direction of the 
	//! vector. Addition is implemented by pairwise addition of the components 
	//! of the quadrilaterals vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator + is 
	//! defined as well and implemented in terms of this operator +=.
	//!
	//! \return A reference to this object.
    sgl::quadrilateral<D,T> & operator += (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		p1_ += rhs;
		p2_ += rhs;
		p3_ += rhs;
		p4_ += rhs;

        return *this;
    }

	/// Subtract a vector from a quadrilateral.
	//!
	//! Subtracting a vector from a quadrilateral moves the quadrilateral in the opposite 
	//! direction of the vector. Subtraction is implemented by pairwise 
	//! subtraction of the components of the quadrilaterals vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator - is 
	//! defined as well and implemented in terms of this operator -=.
	//!
	//! \return A reference to this object.
    sgl::quadrilateral<D,T> & operator -= (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		p1_ -= rhs;
		p2_ -= rhs;
		p3_ -= rhs;
		p4_ -= rhs;

		return *this;
    }

private:
	// The first point of the quadrilateral.
	sgl::point<D,T> p1_;

	// The second point of the quadrilateral.
	sgl::point<D,T> p2_;

	// The third point of the quadrilateral.
	sgl::point<D,T> p3_;

	// The fourth point of the quadrilateral.
	sgl::point<D,T> p4_;
};


/// A two-dimensional quadrilateral with the default component type.
typedef sgl::quadrilateral<2> quadrilateral2;

/// A two-dimensional quadrilateral with float component type.
typedef sgl::quadrilateral<2, float> quadrilateral2f;

/// A two-dimensional quadrilateral with double component type.
typedef sgl::quadrilateral<2, double> quadrilateral2d;


/// A three-dimensional quadrilateral with the default component type.
typedef sgl::quadrilateral<3> quadrilateral3;

/// A three-dimensional quadrilateral with float component type.
typedef sgl::quadrilateral<3, float> quadrilateral3f;

/// A three-dimensional quadrilateral with double component type.
typedef sgl::quadrilateral<3, double> quadrilateral3d;


} // namespace sgl

#endif // __sgl_quadrilateral_h__
